Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track virtual documents. #2436

Merged
merged 4 commits into from
Aug 1, 2018
Merged

Conversation

NTaylorMullen
Copy link
Contributor

  • Track opening, closing and changing of virtual documents that don't exist on disk.
  • Updated the various document selectors to properly include virtual files.

#2431

/cc @DustinCampbell @rchande

- Track opening, closing and changing of virtual documents that don't exist on disk.
- Updated the various document selectors to properly include virtual files.

dotnet#2431
@@ -81,6 +81,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, codeActionProvider));
localDisposables.add(reportDiagnostics(server, advisor));
localDisposables.add(forwardChanges(server));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based a lot of these changes off of the forwardChanges handler and I didn't see any tests. What's the test guidance to test my virtual document tracker in this repo?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't yet have a strong unit testing story for this, but it is possible to write integration tests -- take a look at the integrationTests folder. Not sure if there's an easy way to create virtual documents programmatically without installing another extension though. The "code action rename" test covers this type of scenario.

Copy link

@rchande rchande left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. Generally looks pretty good with a couple of small nits. Also I'm sure @akshita31 will have guidance on the best way to do logging.

let req = { FileName: document.uri.path, changeType: FileChangeType.Create };
serverUtils.filesChanged(server, [req])
.catch(err => {
console.warn(`[o] failed to forward virtual document change event for ${document.uri.path}`, err);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akshita31 Can you weigh in on the best way to do this logging?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should use the C# channel to do the logging here.
@NTaylorMullen The way logging works in the extension , is that we post a loggingEvent to an [https://github.com/OmniSharp/omnisharp-vscode/blob/master/src/EventStream.ts](event stream), to which many observers have susbsribed. When we post the event to the event stream, all the observers that have subscribed to the stream, perform the necessary action. For this case, we will have to add the appropriate logging event and make the C# logger observer listen to it. I am not sure if the C# channel observer , should care of these events and show the channel. I will leave that up to you.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akshita31 How does this component get access to the eventStream?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


function openVirtualDocument(document: TextDocument, server: OmniSharpServer) {
let req = { FileName: document.uri.path, changeType: FileChangeType.Create };
serverUtils.filesChanged(server, [req])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filesChanged and updateBuffer are both async/promise returning and should be awaited

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought so as well but that's not done in the changeForwarding class. Was trying to be consistent. Is it a mistake that it's not awaited there?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, maybe it doesn't matter because your requests get put into the queue in the submission order.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NTaylorMullen Recently we are trying to move to using async/await rather than .then/catch for promises. The changeForwarding still has the old pattern, but for this class we should prefer try/catch and async/await

@@ -81,6 +81,7 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
localDisposables.add(vscode.languages.registerCodeActionsProvider(documentSelector, codeActionProvider));
localDisposables.add(reportDiagnostics(server, advisor));
localDisposables.add(forwardChanges(server));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't yet have a strong unit testing story for this, but it is possible to write integration tests -- take a look at the integrationTests folder. Not sure if there's an easy way to create virtual documents programmatically without installing another extension though. The "code action rename" test covers this type of scenario.

import CompositeDisposable from '../CompositeDisposable';

function trackCurrentVirtualDocuments(server: OmniSharpServer) {
let registration = server.onProjectAdded(() => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to add the existing virtual documents every time O# discovers a project, which can happen multiple times. There is no "done loading" event today, unfortunately.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(We should test this in a multi-project + razor scenario and verify o# doesn't do anything weird if you add a buffer twice)

Copy link
Contributor Author

@NTaylorMullen NTaylorMullen Jul 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is only ever called once for the first project. See at the end of the method where it disposes its own registration to the onProjectAdded.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right. Thanks.

- Moved bits to async await
- Added integration test
- Used event stream APIs
@codecov
Copy link

codecov bot commented Jul 26, 2018

Codecov Report

Merging #2436 into master will increase coverage by 0.71%.
The diff coverage is 63.26%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2436      +/-   ##
==========================================
+ Coverage   63.63%   64.35%   +0.71%     
==========================================
  Files          88       89       +1     
  Lines        4007     4054      +47     
  Branches      567      573       +6     
==========================================
+ Hits         2550     2609      +59     
+ Misses       1294     1272      -22     
- Partials      163      173      +10
Flag Coverage Δ
#integration 53.52% <63.26%> (+0.84%) ⬆️
#unit 84.47% <20%> (-0.25%) ⬇️
Impacted Files Coverage Δ
src/observers/CsharpLoggerObserver.ts 91.37% <0%> (-4.99%) ⬇️
src/omnisharp/extension.ts 80.55% <100%> (+0.36%) ⬆️
src/features/diagnosticsProvider.ts 73.18% <50%> (-3.63%) ⬇️
src/omnisharp/loggingEvents.ts 98.97% <50%> (-1.03%) ⬇️
src/features/virtualDocumentTracker.ts 67.5% <67.5%> (ø)
src/omnisharp/server.ts 71.73% <0%> (-0.71%) ⬇️
src/features/documentation.ts 38.09% <0%> (+2.38%) ⬆️
src/omnisharp/utils.ts 73.68% <0%> (+3.5%) ⬆️
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 82be5e1...51da252. Read the comment docs.

@NTaylorMullen
Copy link
Contributor Author

🆙 📅 covered all feedback.

Tried writing more tests to no avail sadly. Wanted to also verify the closing text document path but that's not supported by VSCode extension APIs (the IDE does it when it wants to). Also wanted to verify the "already opened" scenario but couldn't figure out how to do that because the Omnisharp server is booted once and run throughout all of the integration tests (need to have a virtual document created before server).

@NTaylorMullen
Copy link
Contributor Author

Also, travis failure seems unrelated.

@NTaylorMullen
Copy link
Contributor Author

ping

@rchande
Copy link

rchande commented Jul 31, 2018

@NTaylorMullen Travis failure sounds like it could be related, given that it's in the file change notification layer, which is what's being changed here. Kind of sounds like we're trying to send a file change notification before O# has started of after it has stopped.

o] failed to forward file change event for /home/travis/build/OmniSharp/omnisharp-vscode/test/integrationTests/testAssets/singleCsproj/obj/project.assets.json Error: This socket is closed
    at Socket._writeGeneric (net.js:692:19)
    at Socket._write (net.js:743:8)
    at doWrite (_stream_writable.js:329:12)
    at writeOrBuffer (_stream_writable.js:315:5)
    at Socket.Writable.write (_stream_writable.js:241:11)
    at Socket.write (net.js:670:40)
    at OmniSharpServer._makeRequest (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/src/omnisharp/server.ts:37:2)
    at RequestQueue.OmniSharpServer._requestQueue.requestQueue_1.RequestQueueCollection.request [as _makeRequest] (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/out/src/omnisharp/server.js:8:270)
    at RequestQueue.processPending (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/src/omnisharp/requestQueue.ts:24:38)
    at RequestQueueCollection.drain (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/src/omnisharp/requestQueue.ts:24:38)
    at RequestQueueCollection.enqueue (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/src/omnisharp/requestQueue.ts:24:38)
    at Promise (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/src/omnisharp/server.ts:23:61)
    at Promise (<anonymous>)
    at OmniSharpServer.<anonymous> (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/src/omnisharp/server.ts:23:61)
    at Generator.next (<anonymous>)
    at /home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/out/src/omnisharp/server.js:4:44590
    at Promise (<anonymous>)
    at __awaiter (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/out/src/omnisharp/server.js:4:43765)
    at OmniSharpServer.makeRequest (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/src/omnisharp/server.ts:23:61)
    at Object.<anonymous> (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/out/src/omnisharp/utils.js:4:20562)
    at Generator.next (<anonymous>)
    at /home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/out/src/omnisharp/utils.js:4:18954
    at Promise (<anonymous>)
    at __awaiter (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/out/src/omnisharp/utils.js:4:18089)
    at Object.filesChanged (/home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/out/src/omnisharp/utils.js:4:20461)
    at /home/travis/build/OmniSharp/omnisharp-vscode/vsix/extension/out/src/features/changeForwarding.js:4:6000
    at e.fire (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:96:764)
    at /home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:479:223
    at e.fire (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:96:764)
    at e.$onFileEvent (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:480:375)
    at e._doInvokeHandler (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:636:832)
    at e._invokeHandler (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:636:550)
    at e._receiveRequest (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:635:631)
    at e._receiveOneMessage (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:635:400)
    at /home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:634:315
    at /home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:637:395
    at /home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:95:432
    at e.fire (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:96:764)
    at Socket.<anonymous> (/home/travis/build/OmniSharp/omnisharp-vscode/.vscode-test/stable/VSCode-linux-x64/resources/app/out/vs/workbench/node/extensionHostProcess.js:154:338)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:191:7)
    at readableAddChunk (_stream_readable.js:178:18)
    at Socket.Readable.push (_stream_readable.js:136:10)
    at Pipe.onread (net.js:560:20)

}
}

registration.dispose();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the exact semantics of the NodeJS EventEmitter are. Is it possible for this handler to reenter? If so you have a race on disposing the registration/could process the virtual documents more than once, no?

Copy link
Contributor Author

@NTaylorMullen NTaylorMullen Jul 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the vscode semantics are different but in normal nodejs / JavaScript it's single threaded thus making races impossible.

Copy link
Contributor Author

@NTaylorMullen NTaylorMullen Jul 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, actually, the await yields execution. I can see how things can get invoked more than once unintentionally. Will move the registration dispose to the top to ensure that nothing wonky happens.

@rchande
Copy link

rchande commented Jul 31, 2018

@NTaylorMullen This looks pretty good to me. If the Travis failure isn't obvious to you, we can requeue the build and see if it goes away. If you're interested in getting an "existing virtual document" tested, there are a couple of approaches that might work:

  • Add a hook to all of our existing tests that adds a document provider before we activate the C# extension
  • Issue the "restart OmniSharp" command in your test after adding the document provider

@NTaylorMullen
Copy link
Contributor Author

Thanks for the feedback, I think you were spot on in all accounts. I've addressed all the feedback, verified tests run locally (hopefully travis passes!).

Add a hook to all of our existing tests that adds a document provider before we activate the C# extension
Issue the "restart OmniSharp" command in your test after adding the document provider

Definitely concerned about doing something like that, sounds too extreme. It would muck with any sort of expectations around what documents are currently open in the editor for every other test and could lead to flakeyness in the requirement to restart omnisharp. If you're alright with it I'd rather pass.

🆙 📅

Copy link

@rchande rchande left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@rchande
Copy link

rchande commented Jul 31, 2018

@NTaylorMullen Looks like we got the same build failure for your update...

@NTaylorMullen
Copy link
Contributor Author

NTaylorMullen commented Jul 31, 2018

@NTaylorMullen Looks like we got the same build failure for your update...

Ya I saw that too. I pushed another master branch clone and am seeing if travis fails on that. Those failures don't occur locally.

@NTaylorMullen
Copy link
Contributor Author

@rchande ya same failure on the master branch (without my changes): https://travis-ci.org/OmniSharp/omnisharp-vscode/builds/410456563?utm_source=github_status&utm_medium=notification.

Will that block the inclusion of this PR?

@akshita31
Copy link
Contributor

@NTaylorMullen Yes, currently travis is a mandatory test for merging the pull request. I am investigating further into why the builds are failing.

@rchande
Copy link

rchande commented Jul 31, 2018

@NTaylorMullen Thanks for investigating, we'll take a look.

@akshita31 akshita31 merged commit d00799a into dotnet:master Aug 1, 2018
@akshita31
Copy link
Contributor

@NTaylorMullen The CI issue has been fixed. Thank you for taking the time to contribute to the repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants